#!/usr/bin/env python3
import sys
import signal
import argparse
import struct
import socket
import struct
import threading
#import psutil
import time
import inspect
from datetime import datetime
from threading import Thread
import pdb

local_ip = "127.0.0.1"
local_port =60000
# 定义报文头部结构体格式，使用网络字节序（Big Endian）
UC_PKT_HEADER_LEN = 18
UC_PKT_HEADER_FORMAT = '>B 3s B I B I I'

########################################################################################################################
RESET = "\033[0m"  # 重置所有样式
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
REVCOL = "\033[7m"
BOLD = "\033[1m"
def DLOG(message):
    frame = inspect.stack()[1]
    filename = frame.filename
    lineno = frame.lineno
    timestamp = datetime.now().strftime('%y%m%d %H:%M:%S')
    print(f"{timestamp}| {message}")
    #rint(f"+++: {timestamp} ({filename}:{lineno}) {message}")

def parse_arguments():
    parser = argparse.ArgumentParser(description="cu server")
    parser.add_argument("-p", "--port", type=int, default=60000,
                        help="listen port (default: %(default)s)")
    args = parser.parse_args()
    return args.port

#def get_local_ip_address():
#    connected_nics = psutil.net_if_addrs()
#    connected_ipv4_addresses = []
#    for interface_name, interface_addresses in connected_nics.items():
#        for addr in interface_addresses:
#            if addr.family == socket.AF_INET and addr.address and addr.address != '127.0.0.1':
#                connected_ipv4_addresses.append(addr.address)
#    return connected_ipv4_addresses
#
########################################################################################################################
def signal_handler(sig, frame):
    print("Received SIGINT, shutting down server...")
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

def parse_header(header_data):
    try: 
        ver, proto_signature, msgtype, msgno, resv1, msg_seqno, msg_length = struct.unpack(UC_PKT_HEADER_FORMAT, header_data)
        if proto_signature != b'CUC':
            raise ValueError("Invalid proto_signature")
        resv = ver >> 4
        return {
            #'ver': ver & 0x0F,
            #'resv': resv,
            'proto_signature': proto_signature.decode(),
            'msgtype': msgtype,
            'msgno': msgno,
            #'resv1': resv1,
            'msg_seqno': msg_seqno,
            'msg_length': msg_length,
        }
    except ValueError as e:
        DLOG(f"Error parsing header: {e}")
        raise 

def send_response_to_client(conn, addr, pkt_head_info, errstatus=0x00, errInfoStr=b'12345'):
    client_ip, client_port = addr[:2]
    resp_msgtype = 0xcd
    resp_msgno = pkt_head_info['msgno']
    resp_msgseq = pkt_head_info['msg_seqno']
    errInfoLen = len(errInfoStr)
    # 手动拼接字节串
    resp_body = bytes([resp_msgtype]) + \
        bytes(struct.pack('!I', resp_msgno)) + \
        bytes(struct.pack('!I', resp_msgseq)) + \
        bytes([errstatus]) + \
        bytes(struct.pack('!H', errInfoLen)) + \
        errInfoStr
    resp_total_data_len = UC_PKT_HEADER_LEN + len(resp_body)

    # 消息头
    ver = 0b0011  # 假设 ver 的值为 3
    resv = 0b0001  # 假设 resv 的值为 1
    ver_resv = (ver << 4) | resv
    resp_pkt_head_info = {
        'ver_resv': ver_resv,
        'proto_signature': b'CUC',
        'msgtype': resp_msgtype,
        'msgno': resp_msgno,
        'resv1': 0,
        'msg_seqno': resp_msgseq,
        'msg_length': resp_total_data_len,
    }
    resp_pkt_head_data = struct.pack(UC_PKT_HEADER_FORMAT, *resp_pkt_head_info.values())
    # send response
    resp_pkt_all_data = resp_pkt_head_data + resp_body
    DLOG(REVCOL + f"server: :{local_port}-->{client_ip}:{client_port}: send: {hex(resp_msgtype)}({resp_total_data_len} bytes): {resp_pkt_all_data.hex()}" + RESET)
    conn.sendall(resp_pkt_all_data)

active_connections_with_stats = {}

def signal_handler(sig, frame):
    DLOG("Received SIGINT, shutting down server...")
    for conn, total_recv_len in active_connections_with_stats.items():
        try:
            conn.shutdown(socket.SHUT_RDWR)  # 尝试优雅地关闭连接
            conn.close()
            DLOG("Received SIGINT, shutting down server: 111...")
            DLOG("Connection stats before close: IP/Port - Total Data Received: {conn.getpeername()} - {total_recv_len}")
        except Exception as e:
            DLOG("Received SIGINT, shutting down server: 222...")
            DLOG(f"Error closing connection: {e}")
    DLOG("Received SIGINT, shutting down server: 333...")
    sys.exit(0)

def handle_client(conn, addr):
    client_ip, client_port = addr[:2]
    DLOG(f"server: {local_ip}:{local_port}<--{client_ip}:{client_port}: connected")

    buffer = b''  # 初始化缓冲区
    total_recv_len = 0
    counter = 0
    while True:
        received_data = conn.recv(10240)
        if not received_data:
            DLOG(f"server: {local_ip}:{local_port}<--{client_ip}:{client_port}: client socket closed.")
            break
        counter += 1
        total_recv_len += len(received_data)
        active_connections_with_stats[conn] = total_recv_len
        DLOG(f"server: :{local_port}<--{client_ip}:{client_port}: counter:{counter}: recv_len:{total_recv_len}")
        #f counter % 1000 == 0: 
        #   DLOG(f"server: :{local_port}<--{client_ip}:{client_port}: counter:{counter}: recv_len:{total_recv_len}")
    DLOG(f"Closing connection from {addr}")
    conn.close()

def tcp_listener():
    """
    创建TCP服务器并监听指定端口，为每个连接的客户端创建独立的处理线程。
    """
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((local_ip, local_port))
        s.listen(10) 
        DLOG(f"server: XX:{local_port}: listening: ...")

        while True:
            conn, addr = s.accept()
            threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

if __name__ == "__main__":
    if len(sys.argv) < 2:
        DLOG(f"usage: argv[0] <ip> <port>\n")
        sys.exit(1)
    DLOG(f"args={sys.argv}")
    local_ip  	= sys.argv[1]
    local_port	= int(sys.argv[2])
    tcp_listener()
